
#include "CControlConnection.h"
#include "MGpsObserver.h"

_LIT8(KHelloMessage,"NODE: 02Binary\n"); /* TODO: needs to read the nodeid from the config file */

CControlConnection::CControlConnection(CConfig& aConfig, MDisplayObserver& aDisplay):CActive(EPriorityStandard),iConfig(aConfig),iDisplay(aDisplay)
{

}

CControlConnection::~CControlConnection()
{
	#ifdef __LOGGER
			iLog.Write(_L("Destroying CControlConnection."));
	#endif	
	
	this->Stop();
	
	if (iDataConnection){
		delete iDataConnection;
		iDataConnection=NULL;
	}	
	
	if (iBtEngine) {
		delete iBtEngine;
		iBtEngine=NULL;
	}

	if (iGprsEngine) {
		delete iGprsEngine;
		iGprsEngine=NULL;
	}	
	/* iSocketServ is opened and closed by iGprsEngine */
	if (iSocketServ)
	{
		if (iSocketServ) {
			delete iSocketServ;
			iSocketServ=NULL;
		}
	}
	
	iTimmer.Close(); /* Important */
	
	#ifdef __LOGGER
		iLog.Write(_L("CControlConnection destroyed"));
		iLog.CloseLog();
		iLog.Close();
	#endif
}

CControlConnection *CControlConnection::NewL(CConfig &aConfig, MDisplayObserver& aDisplay)
{
	CControlConnection * self = new (ELeave) CControlConnection (aConfig, aDisplay);
	CleanupStack::PushL( self );
	self->ConstructL(aConfig);
	CleanupStack::Pop();
	return self;	
}

void CControlConnection::ConstructL(CConfig& aConfig)
{
	CActiveScheduler::Add( this );
	aConfig.get_GprsIAPL(iIAP);
  	iSocketServ = new (ELeave) RSocketServ();
  	
  	#ifdef __EMULATOR 	
  		iGprsEngine = CGprsEmulator::NewL(*iSocketServ);
  	#else
  		iGprsEngine = CGprsEngine::NewL(iIAP,*iSocketServ);
  	#endif

  	//Load the configuration
  	aConfig.get_nodeIdL(iNodeid);
  	aConfig.get_control_portL(iPort);
  	aConfig.get_control_reconnection_interval(iIintervalReconnection);
  	aConfig.get_serverL(iServerName);
  	iDataServerName.Zero();
  	iDataServerName.Copy(iServerName);
  	aConfig.get_data_portL(iDataPort);
  	aConfig.get_intervalL(iDataInterval);
  	aConfig.get_gps_addressL(iGpsAddr);
  	
  	iBtEngine = CBTEngine::NewL(iGpsAddr, 1, iDisplay);
  	iDataConnection = CDataConnection::NewL(*iSocketServ, iDataServerName, iNodeid, iDataPort, iDataInterval, iDisplay, (MGpsObserver&)*iBtEngine);

  	User::LeaveIfError(iTimmer.CreateLocal());
  	
	iState = EIdle;
  	
	#ifdef __LOGGER
	  	iLog.Connect();
		iLog.CreateLog(_L("findme"),_L("CControlConnection.txt"),EFileLoggingModeOverwrite);
		iLog.Write(_L("CControlConnection loaded."));
	#endif
}

TInt CControlConnection::StartManually() 
{
	if (iState == EIdle) {
		TInt err = 0;
		iDisplay.DisplayStatus(EGprs, _L("Connecting"));
		err = iGprsEngine->OpenConnection();
		//It waits until the connection is established
		if (err != KErrNone) {
			iDisplay.DisplayStatus(EGprs, _L("Failed"));
			return err;
		}
		iDisplay.DisplayStatus(EGprs, _L("Connected"));
		err = iDataConnection->Start();
		if (err != KErrNone) {
			iDisplay.DisplayStatus(EGprs, _L("Error"));
			StopManually();
			return err;
		} 
		iState = EManuallyConnected;
	}
	return KErrNone;
}

void CControlConnection::StopManually()
{	
	iDataConnection->Stop();
	iGprsEngine->CloseConnection();
	iDisplay.DisplayStatus(EGprs, _L("Disconnected"));
	iDisplay.DisplayStatus(EStatus, _L("Idle"));
}

TInt CControlConnection::Start() 
{
	if (iState == EIdle) {
		TInt err = 0;
		#ifdef __LOGGER
			iLog.Write(_L("Starting GprsConnection..."));
		#endif
		err = iGprsEngine->OpenConnection();
		//It wait until the connection is established
		if (err != KErrNone) {
			return err;
		}
		#ifdef __LOGGER
			iLog.Write(_L("GprsConnection started ok."));
		#endif
		
		err = iDataConnection->Start();
		if (err != KErrNone) {
			#ifdef __LOGGER
				iLog.WriteFormat(_L("ERROR: iDataConnection.Start returned : %d"), err);
			#endif
		}
		
		OpenConnection();
		return KErrNone;
	}
	#ifdef __LOGGER
		iLog.Write(_L("CControl connection already started."));
	#endif
	return KErrNone;
}


void CControlConnection::Stop()
{	
	iDataConnection->Stop();
	if(IsActive()) {
		#ifdef __LOGGER
			iLog.Write(_L("CActive was active. Canceling..."));
		#endif
		Cancel(); /* It calls DoCancel() */
	}
	CloseConnection();
	iGprsEngine->CloseConnection();
}

/*
 * Try to resolv the name given by iServerName
 */
void CControlConnection::OpenConnection()
{
	if (iState == EIdle)
	{
		RHostResolver iResolver ;
		TInt err;
		#ifdef __LOGGER
			iLog.Write(_L("Connecting to :"));
			iLog.Write(iServerName);
		#endif

		err = iResolver.Open(*iSocketServ, KAfInet, KProtocolInetTcp);
		if (err != KErrNone) {
			#ifdef __LOGGER
				iLog.WriteFormat(_L("Error opening iResolver:%d"),err);
			#endif
			SetActive();
			TRequestStatus* s=&iStatus;
			User::RequestComplete(s, err);
			return;
		}
		
		TRequestStatus aStatus;
		iResolver.GetByName(iServerName, iHostAddress, aStatus);
		User::WaitForRequest(aStatus);
		iResolver.Close();
		if (aStatus != KErrNone) {
			#ifdef __LOGGER
				iLog.WriteFormat(_L("error: dns lookup failed:%d"),aStatus);
			#endif
			SetActive();
			TRequestStatus* s=&iStatus;
			User::RequestComplete(s, aStatus.Int());
			return;
		}
		
		iAddress = iHostAddress().iAddr;
		iAddress.SetPort(iPort);
		#ifdef __LOGGER
			iLog.WriteFormat(_L("Port = %d"),iPort);			
		#endif
		// Attempt to open the socket
		err = iSocket.Open(*iSocketServ, KAfInet, KSockStream, KProtocolInetTcp);
		if (err != KErrNone) {
			#ifdef __LOGGER
				iLog.WriteFormat(_L("Error opening iSocket:%d"),err);
			#endif
			SetActive();
			TRequestStatus* s=&iStatus;
			User::RequestComplete(s, err);
			return;
		}
		
		iSocket.Connect(iAddress, iStatus);
		iState=EConnecting;
		SetActive();
	}
}


/*
 * Close the active connection
 */
void CControlConnection::CloseConnection()
{
	if (iState != EIdle)
	{
			iSocket.Close();
			iState = EIdle;	
			#ifdef __LOGGER
				iLog.Write(_L("Connection closed."));
			#endif	
	}
}

void CControlConnection::DoCommandL()
{
	#ifdef __LOGGER
		iLog.Write(_L("Do Comand:"));
		iLog.Write(iMessage);
	#endif
		
	if (iMessage.Compare(_L("exit\n")) == 0)
	{
			iMessage.Zero(); //Remember do that in all commands!
			Stop();					
	}
	else if (iMessage.Compare(_L("help\n")) == 0)
	{
				iMessage.Zero(); //Remember do that in all commands!
				iMessageToSend.Copy(_L8("Commands : start, stop, interval secs, stats, exit, reboot.\n"));
				SendMessage();
	}
	//Start the data transfer
	/*
	else if (iMessage.Compare(_L("start\n")) == 0)
	{
			iMessage.Zero(); //Remember do that in all commands!
			if (iDataConnection->IsRunning() == 0)
			{
				iDataConnection->Start();
				iMessageToSend.Zero();
				iMessageToSend.Copy(_L8("The data transfer has started.\n"));
				SendMessage();
			}
			else
			{
				iMessageToSend.Zero();
				iMessageToSend.Copy(_L8("The data transfer is already started.\n"));
				SendMessage();
			}
	} */
	//Stop the data transfer
	/*
	else if (iMessage.Compare(_L("stop\n")) == 0)
	{
			iMessage.Zero(); //Remember do that in all commands!
			if (iDataConnection->IsRunning()== EFalse)
			{
				iMessageToSend.Zero();
				iMessageToSend.Copy(_L8("The data transfer was stopped.\n"));
				SendMessage();
			}
			else
			{
				iDataConnection->Stop();
				iMessageToSend.Zero();
				iMessageToSend.Copy(_L8("The data transfer has been stopped.\n"));
				SendMessage();
			}
	}
	*/
	//Get the amount of data transferred
	else if (iMessage.Compare(_L("stats\n")) == 0)
	{
			#ifdef __LOGGER
				iLog.Write(_L("Obteniendo stats"));
				iLog.Write(iMessage);
			#endif
			iMessage.Zero(); //Remember do that in all commands!
			TBuf8<32> iUp;
			TBuf8<32> iDown;
			iGprsEngine->DataTransferredRequest(iUp,iDown);
			iMessageToSend.Zero();
			iMessageToSend.Copy(_L8("Up/Down (bytes): "));
			iMessageToSend.Append(iUp);
			iMessageToSend.Append(_L8("/"));
			iMessageToSend.Append(iDown);
			iMessageToSend.Append(_L8("\n"));
			SendMessage();				
	}
	//Change the interval
	else if (iMessage.Match(_L("interval *")) != KErrNotFound)
	{
			TInt interval;
			interval = get_interval_from_command(iMessage);
			if (interval > 0)
			{
				iMessage.Zero(); //Remember do that in all commands!
				// iDataConnection->SetInterval(interval); //
				iMessageToSend.Zero();
				iMessageToSend.Copy(_L8("The interval of data transfer has been changed.\n"));
				SendMessage();
			}
			else
			{
				iMessage.Zero(); //Remember do that in all commands!
				iMessageToSend.Zero();
				iMessageToSend.Copy(_L8("Incorrect interval.\n"));
				SendMessage();
			}
	}
	else if (iMessage.Compare(_L("reboot\n")) == 0)
	{
			#ifdef __LOGGER
				iLog.Write(_L("Rebooting the system"));
				iLog.Write(iMessage);
			#endif
			iMessage.Zero(); //Remember do that in all commands!
			Stop();
			//Info http://www.forum.nokia.com/info/sw.nokia.com/id/8b87edfe-fb18-4a2c-9e71-0b4852d0a25e/2nd_Edition_Platforms_Known_Issues_v2_4_en.pdf.html
			//I need to be carefull if I forget to close some file.
			//Data collector has a file open in this point.
			//I need to close it before the mobile restart
			delete iDataConnection;
			UserSvr::ResetMachine(EStartupWarmReset);
			//The next line doesn't work
			//UserSvr::ResetMachine(EStartupSafeReset);		
	}
	else
	{
		iMessage.Zero(); //Remember do that in all commands!
		iMessageToSend.Zero();
		iMessageToSend.Copy(_L8("Command not found.\n"));
		SendMessage();
	}
}

void CControlConnection::RunL()
{
	#ifdef __LOGGER
		iLog.WriteFormat(_L("RunL. iState/iStatus = %d/%d"), iState,iStatus);
	#endif
	if (iStatus == KErrNone)
	{
		switch (iState)
		{
		case EIdle:
			// Shouldn't happen
			break ;
		case EConnecting:
			//The connection is stablished
			#ifdef __LOGGER
				iLog.Write(_L("Connection has been stablished"));
			#endif
			iMessageToSend=KHelloMessage;
			SendMessage();
			break;
		case ESending:
			//The message was sent.
			WaitForAMessage();	
			break;
		case EReceiving:
			//I'm receiving chunks of data
			ManageChunks();
			break;
		case EReceived:
			//We have the full command
			DoCommandL();
			break;
		case EReconnecting:
			CloseConnection();
			OpenConnection();
			break;
		default:
			break;
		}
	}
	/* Usefull links about KErrCancel  
	 * http://discussion.forum.nokia.com/forum/showthread.php?t=122781 */
	else if (iStatus == KErrCancel)
	{
		#ifdef __LOGGER
			iLog.Write(_L("ERROR: Active object canceled."));
		#endif
	}
	else if (iStatus == KErrCouldNotConnect )
	{
		#ifdef __LOGGER
			iLog.Write(_L("Control port closed"));
			iLog.WriteFormat(_L("Waiting = %d seconds"), iIintervalReconnection);
		#endif
		iState = EReconnecting;
		iTimmer.After(iStatus,iIintervalReconnection * 1000000); 
		SetActive();	
	}
	else
	{
		#ifdef __LOGGER
			iLog.WriteFormat(_L("unknown error code=%d"),iStatus);
			iLog.Write(_L("Maybe GPRS connection broken. Stopping..."));
		#endif	
		this->Stop();	
	}	
}


void CControlConnection::DoCancel()
{
	#ifdef __LOGGER
		iLog.Write(_L("Inside DoCancel."));
	#endif
	
	if (iState == EConnecting || iState == ESending || iState == EReceiving) 
	{
		iSocket.CancelAll();
		#ifdef __LOGGER
			iLog.Write(_L("iSocket cancelled."));
		#endif
	}	
	else if (iState == EReconnecting) 
	{
		iTimmer.Cancel();
		#ifdef __LOGGER
			iLog.Write(_L("iTimmer cancelled."));
		#endif
	}
	else
	{
		#ifdef __LOGGER
			iLog.Write(_L("Error: Nothing to cancel."));
		#endif
	}
}

/*
 * Send iMessagetoSend to the other point of the connection
 */
void CControlConnection::SendMessage()
{
	if (iMessageToSend.Length() > 0)
	{
		iSocket.Write(iMessageToSend, iStatus) ;
		iState = ESending ;
		SetActive();
	}	
}


/* 
 * Receive a chunk from the other point of the connection
 */
void CControlConnection::WaitForAMessage()
{
	iResponseChunk.Zero(); //Reset the data
	iSocket.RecvOneOrMore(iResponseChunk, 0, iStatus, iResponseChunkSizePkg);
	iState=EReceiving;
	SetActive();
}
/*
 * Put together all the chunks until the KEndCommand is received
 */
void CControlConnection::ManageChunks()
{
	#ifdef __LOGGER
		iLog.WriteFormat(_L("Chunk len: %d"),iResponseChunk.Length());
		iLog.WriteFormat(_L("Message len: %d"),iMessage.Length());
		iLog.WriteFormat(_L("Message size: %d"),iMessage.MaxLength());
	#endif
	if (iResponseChunk.Length() + iMessage.Length() <= iMessage.MaxLength())
	{
		#ifdef __LOGGER
			iLog.Write(_L("Adding chunk to the msg"));
		#endif
		HBufC16* hMessage = HBufC16::NewL(iResponseChunk.Length());
		CleanupStack::PushL(hMessage);
		TPtr16 ptr(hMessage->Des());
		ptr.Copy(iResponseChunk);
		iMessage.Append(ptr);
		//iMessage.Append(iResponseChunk);
		CleanupStack::PopAndDestroy(hMessage);
		//delete hMessage;
		hMessage=NULL;
		#ifdef __LOGGER
			iLog.Write(_L("Chunk added"));
		#endif
		_LIT(KEndCommand, "\n");
		TInt err = iMessage.Find(KEndCommand);
		if (err == KErrNotFound)
		{
			#ifdef __LOGGER
				iLog.Write(_L("Need more chunks"));
			#endif
			WaitForAMessage(); //We need more chunks
		}
		else
		{
			//It has the command
			iState=EReceived;
			SetActive();
			TRequestStatus* s=&iStatus;
			User::RequestComplete(s, KErrNone);	
		}
	}
	else
	{
		//Message execed the maximun size
		iMessage.Zero();
		WaitForAMessage();
	}	 		
}

TInt CControlConnection::get_interval_from_command(TDesC &aMessage)
{
	TInt pos;
	TInt interval;
	
	pos = aMessage.Find(_L(" "));
	//Len("interval ") = 9 
	if (pos != 8) return -1;
	HBufC *temp = HBufC::NewL(aMessage.Size() - pos);
	//CleanupStack::PushL(temp);
	TPtr pt(temp->Des());
	pt.Copy(aMessage.Right(aMessage.Length() - pos -1)); //-1 removes the blank space
	TLex string(pt); //Convert string to TInt
	string.Val(interval);
	
	delete temp;
	temp=NULL;
	
	return interval;
}

void CControlConnection::setDataConnectionParameters(const TDesC* aDataServerName, TUint aNodeid , TUint aDataPort, TUint aDataInterval)
{
	if (aDataServerName != NULL)
		iDataConnection->setServer(*aDataServerName);
	if (aNodeid > 0) 
		iDataConnection->setNodeid(aNodeid);
	if (aDataPort > 0)
		iDataConnection->setPort(aDataPort);
	if (aDataInterval > 0)
		iDataConnection->setInterval(aDataInterval);
} 

void CControlConnection::setGpsParameters(const TBTDevAddr& aAddr, const TUint& aPort)
{
	iGpsAddr.Reset();
	iGpsAddr = aAddr;
	iGpsPort = aPort;
	iBtEngine->setBtDevice(aAddr, aPort);
}

CBTEngine* CControlConnection::getBtEngine()
{
	return iBtEngine;
}